package ru.slovoslovo.cropmotion.ui;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeExpansionEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.MessageBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.slovoslovo.cropmotion.CropSettings;
import ru.slovoslovo.cropmotion.DestinationMode;
import ru.slovoslovo.cropmotion.TemplateApplyMode;
import ru.slovoslovo.cropmotion.cropping.CropTemplate;
import ru.slovoslovo.cropmotion.cropping.Cropper;
import ru.slovoslovo.cropmotion.ui.filetable.TableContentProvider;
import ru.slovoslovo.cropmotion.ui.filetable.TableLabelProvider;
import ru.slovoslovo.cropmotion.ui.filetree.FileTreeNode;
import ru.slovoslovo.cropmotion.ui.filetree.FileViewerSorter;
import ru.slovoslovo.cropmotion.ui.filetree.TreeContentProvider;
import ru.slovoslovo.cropmotion.ui.filetree.TreeLabelProvider;

public class MainWindow extends ApplicationWindow {
    private static final Logger LOG = LoggerFactory.getLogger(MainWindow.class);

    private TreeViewer treeViewerFile;
    private TableViewer tableViewerFile;
    private Table tableFile;
    private Text textDestPath;
    private Action actionRun;
    private Action actionExit;
    private Button btnBrowse;
    private ComboViewer comboViewerTemplateApplyMode;
    private ComboViewer comboViewerDestMode;
    private CropSettings settings;

    /**
     * Create the application window.
     */
    public MainWindow(CropSettings settings) {
        super(null);
        this.settings = (settings != null) ? settings : new CropSettings();
        createActions();
        addToolBar(SWT.FLAT | SWT.WRAP);
        addMenuBar();
        addStatusLine();
    }

    /**
     * Create contents of the application window.
     * 
     * @param parent
     */
    @Override
    protected Control createContents(Composite parent) {
        Composite mainContainer = new Composite(parent, SWT.NONE);
        FormLayout fl_mainContainer = new FormLayout();
        fl_mainContainer.marginRight = 10;
        fl_mainContainer.marginLeft = 10;
        fl_mainContainer.spacing = 10;
        mainContainer.setLayout(fl_mainContainer);

        SashForm sashForm = new SashForm(mainContainer, SWT.NONE);
        FormData fd_sashForm = new FormData();
        fd_sashForm.top = new FormAttachment(0, 10);
        fd_sashForm.left = new FormAttachment(0);
        fd_sashForm.right = new FormAttachment(100);
        sashForm.setLayoutData(fd_sashForm);

        treeViewerFile = new TreeViewer(sashForm, SWT.BORDER);
        treeViewerFile.setContentProvider(new TreeContentProvider());
        treeViewerFile.setLabelProvider(new TreeLabelProvider());
        treeViewerFile.setInput(new FileTreeNode(null));
        treeViewerFile.setSorter(new FileViewerSorter());

        TableColumnLayout layout = new TableColumnLayout();
        sashForm.setLayout(layout);

        tableViewerFile = new TableViewer(sashForm, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
        tableViewerFile.setColumnProperties(new String[] {});

        Table table = tableViewerFile.getTable();
        table.setLinesVisible(true);
        table.setHeaderVisible(true);

        TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewerFile, SWT.NONE);
        TableColumn tblclmnPixel100 = tableViewerColumn.getColumn();
        tblclmnPixel100.setWidth(200);
        //Specify width using pixels
        layout.setColumnData(tblclmnPixel100, new ColumnPixelData(100, true, true));
        tblclmnPixel100.setText("Name");

        TableViewerColumn tableViewerColumn_1 = new TableViewerColumn(tableViewerFile, SWT.NONE);
        TableColumn tblclmnWeight2 = tableViewerColumn_1.getColumn();
        tblclmnWeight2.setWidth(50);
        //Specify width using weights
        layout.setColumnData(tblclmnWeight2, new ColumnWeightData(2, ColumnWeightData.MINIMUM_WIDTH, true));
        tblclmnWeight2.setText("Type");

        TableViewerColumn tableViewerColumn_2 = new TableViewerColumn(tableViewerFile, SWT.NONE);
        TableColumn tblclmnWeight4 = tableViewerColumn_2.getColumn();
        tblclmnWeight4.setWidth(70);
        //Specify width using weights
        layout.setColumnData(tblclmnWeight4, new ColumnWeightData(4, ColumnWeightData.MINIMUM_WIDTH, true));
        tblclmnWeight4.setText("Size");

        TableViewerColumn tableViewerColumn_3 = new TableViewerColumn(tableViewerFile, SWT.NONE);
        TableColumn tblclmnWeight6 = tableViewerColumn_3.getColumn();
        tblclmnWeight6.setWidth(70);
        //Specify width using weights
        layout.setColumnData(tblclmnWeight6, new ColumnWeightData(6, ColumnWeightData.MINIMUM_WIDTH, true));
        tblclmnWeight6.setText("Date");

        tableViewerFile.setContentProvider(new TableContentProvider());
        tableViewerFile.setLabelProvider(new TableLabelProvider());

        TabFolder tabFolderProgress = new TabFolder(mainContainer, SWT.NONE);
        fd_sashForm.bottom = new FormAttachment(tabFolderProgress, -6);
        sashForm.setWeights(new int[] {477, 444});
        FormData fd_tabFolderProgress = new FormData();
        fd_tabFolderProgress.left = new FormAttachment(0);
        fd_tabFolderProgress.right = new FormAttachment(100);
        fd_tabFolderProgress.bottom = new FormAttachment(100);
        fd_tabFolderProgress.top = new FormAttachment(100, -145);
        tabFolderProgress.setLayoutData(fd_tabFolderProgress);

        TabItem tbtmProgress = new TabItem(tabFolderProgress, SWT.NONE);
        tbtmProgress.setText("Progress");

        Composite compositeProgress = new Composite(tabFolderProgress, SWT.NONE);
        tbtmProgress.setControl(compositeProgress);
        compositeProgress.setLayout(new FormLayout());

        ProgressBar progressBarOveral = new ProgressBar(compositeProgress, SWT.NONE);
        FormData fd_progressBarOveral = new FormData();
        fd_progressBarOveral.bottom = new FormAttachment(100, -10);
        progressBarOveral.setLayoutData(fd_progressBarOveral);

        Group grpDestination = new Group(compositeProgress, SWT.NONE);
        fd_progressBarOveral.right = new FormAttachment(grpDestination, 0, SWT.RIGHT);
        fd_progressBarOveral.left = new FormAttachment(grpDestination, 0, SWT.LEFT);
        grpDestination.setText("Destination");
        grpDestination.setLayout(new FormLayout());
        FormData fd_grpDestination = new FormData();
        fd_grpDestination.left = new FormAttachment(0, 10);
        fd_grpDestination.right = new FormAttachment(100, -10);
        fd_grpDestination.top = new FormAttachment(0, 10);
        fd_grpDestination.bottom = new FormAttachment(0, 77);
        grpDestination.setLayoutData(fd_grpDestination);

        comboViewerDestMode = new ComboViewer(grpDestination, SWT.READ_ONLY);
        comboViewerDestMode.setContentProvider(ArrayContentProvider.getInstance());
        comboViewerDestMode.setInput(new DestinationMode[] {DestinationMode.SAME_AS_ORIGINAL, DestinationMode.IN_SUBFOLDER,
        DestinationMode.SPECIFIED_PATH});

        Combo comboDestMode = comboViewerDestMode.getCombo();
        FormData fd_comboDestType = new FormData();
        fd_comboDestType.top = new FormAttachment(0, 10);
        fd_comboDestType.left = new FormAttachment(0, 10);
        fd_comboDestType.bottom = new FormAttachment(100, -51);
        fd_comboDestType.right = new FormAttachment(0, 204);
        comboDestMode.setLayoutData(fd_comboDestType);

        textDestPath = new Text(grpDestination, SWT.BORDER);
        FormData fd_textDestPath = new FormData();
        fd_textDestPath.left = new FormAttachment(comboDestMode, 20);
        fd_textDestPath.top = new FormAttachment(comboDestMode, 0, SWT.TOP);
        textDestPath.setLayoutData(fd_textDestPath);

        btnBrowse = new Button(grpDestination, SWT.NONE);
        fd_textDestPath.right = new FormAttachment(100, -164);
        FormData fd_btnBrowse = new FormData();
        fd_btnBrowse.right = new FormAttachment(100, -34);
        fd_btnBrowse.left = new FormAttachment(textDestPath, 33);
        fd_btnBrowse.top = new FormAttachment(comboDestMode, -2, SWT.TOP);
        btnBrowse.setLayoutData(fd_btnBrowse);
        btnBrowse.setText("Browse");

        TabItem tbtmOptions = new TabItem(tabFolderProgress, SWT.NONE);
        tbtmOptions.setText("Options");

        Composite composite = new Composite(tabFolderProgress, SWT.NONE);
        tbtmOptions.setControl(composite);
        composite.setLayout(new FormLayout());

        Label lblTemplateApplyingOrder = new Label(composite, SWT.NONE);
        FormData fd_lblTemplateApplyingOrder = new FormData();
        fd_lblTemplateApplyingOrder.right = new FormAttachment(0, 128);
        fd_lblTemplateApplyingOrder.top = new FormAttachment(0, 10);
        fd_lblTemplateApplyingOrder.left = new FormAttachment(0, 10);
        lblTemplateApplyingOrder.setLayoutData(fd_lblTemplateApplyingOrder);
        lblTemplateApplyingOrder.setText("Template applying");

        comboViewerTemplateApplyMode = new ComboViewer(composite, SWT.READ_ONLY);
        comboViewerTemplateApplyMode.setContentProvider(ArrayContentProvider.getInstance());
        comboViewerTemplateApplyMode.setInput(new TemplateApplyMode[] {TemplateApplyMode.BY_ORDER, TemplateApplyMode.RANDOM,
        TemplateApplyMode.ALL_FOR_EACH});

        FormData fd_combo = new FormData();
        fd_combo.right = new FormAttachment(0, 172);
        fd_combo.top = new FormAttachment(0, 31);
        fd_combo.left = new FormAttachment(0, 10);
        comboViewerTemplateApplyMode.getCombo().setLayoutData(fd_combo);

        TabItem tbtmLog = new TabItem(tabFolderProgress, SWT.NONE);
        tbtmLog.setText("Log");

        StyledText styledText = new StyledText(tabFolderProgress, SWT.BORDER);
        styledText.setText("preparing\r\nstarting\r\nprocessing\r\nfinishing\r\ndone\r\n");
        tbtmLog.setControl(styledText);

        createListeners();
        applySettings();

        return mainContainer;
    }

    /**
     * Change dest mode
     */
    @SuppressWarnings("incomplete-switch")
    private void changeDestMode(DestinationMode to) {
        switch (to) {
            case IN_SUBFOLDER:
                switch (settings.getDestinationMode()) {
                    case SPECIFIED_PATH:
                        settings.setDestinationPath(textDestPath.getText());
                        break;
                }
                textDestPath.setText(settings.getSubfolderPath());
                textDestPath.setVisible(true);
                textDestPath.setEnabled(true);
                btnBrowse.setVisible(false);
                break;
            case SAME_AS_ORIGINAL:
                switch (settings.getDestinationMode()) {
                    case IN_SUBFOLDER:
                        settings.setSubfolderPath(textDestPath.getText());
                        break;
                    case SPECIFIED_PATH:
                        settings.setDestinationPath(textDestPath.getText());
                        break;
                }
                textDestPath.setText("");
                textDestPath.setVisible(false);
                btnBrowse.setVisible(false);
                break;
            case SPECIFIED_PATH:
                switch (settings.getDestinationMode()) {
                    case IN_SUBFOLDER:
                        settings.setSubfolderPath(textDestPath.getText());
                        break;
                }
                textDestPath.setText(settings.getDestinationPath());
                textDestPath.setVisible(true);
                textDestPath.setEnabled(true);
                btnBrowse.setVisible(true);
                break;
        }
        settings.setDestinationMode(to);
    }

    /**
     * Create the contents listeners
     */
    private void createListeners() {
        comboViewerDestMode.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                changeDestMode((DestinationMode) ((IStructuredSelection) event.getSelection()).getFirstElement());
            }
        });

        comboViewerTemplateApplyMode.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                settings.setTemplateApplyMode((TemplateApplyMode) ((IStructuredSelection) event.getSelection()).getFirstElement());
            }
        });

        treeViewerFile.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                FileTreeNode node = (FileTreeNode) ((IStructuredSelection) event.getSelection()).getFirstElement();
                tableViewerFile.setInput(node.getFile());
                settings.setSourcePath(node.getFile().getAbsolutePath());
            }
        });

        treeViewerFile.addTreeListener(new ITreeViewerListener() {
            @Override
            public void treeExpanded(TreeExpansionEvent paramTreeExpansionEvent) {
                FileTreeNode node = (FileTreeNode) paramTreeExpansionEvent.getElement();
                tableViewerFile.setInput(node.getFile());
            }

            @Override
            public void treeCollapsed(TreeExpansionEvent paramTreeExpansionEvent) {

            }
        });

        textDestPath.addModifyListener(new ModifyListener() {

            @Override
            public void modifyText(ModifyEvent event) {
                switch (settings.getDestinationMode()) {
                    case IN_SUBFOLDER:
                        settings.setSubfolderPath(((Text) event.getSource()).getText());
                        break;
                    case SPECIFIED_PATH:
                        settings.setDestinationPath(((Text) event.getSource()).getText());
                        break;
                    default:
                        break;
                }
            }
        });
    }

    private void applySettings() {
        textDestPath.setText(settings.getDestinationPath());
        comboViewerDestMode.setSelection(new StructuredSelection(settings.getDestinationMode()));
        comboViewerTemplateApplyMode.setSelection(new StructuredSelection(settings.getTemplateApplyMode()));
        File sourceFile = new File(settings.getSourcePath());
        if (sourceFile.exists()) {
            tableViewerFile.setInput(sourceFile);
            //TODO
            //treeViewerFile.setExpandedTreePaths(new TreePath[]});
        }
    }

    /**
     * Create the actions.
     */
    private void createActions() {
        // Create the actions
        actionRun = new Action("Run") {
            public void run() {
                List<File> files = new ArrayList<File>();

                for (Object selectedObject : ((IStructuredSelection) tableViewerFile.getSelection()).toArray()) {
                    files.add((File) selectedObject);
                }

                Cropper cropper = new Cropper(settings);

                List<CropTemplate> cropTemplates = CropTemplate.loadTemplates(CropTemplate.LOCATION);
                if (cropTemplates.isEmpty()) {
                    MessageBox messageBox = new MessageBox(getShell(), SWT.ICON_INFORMATION | SWT.OK);
                    messageBox.setMessage("Crop tempaltes not found, please put it into program 'templates' subdirectory, please.");
                    messageBox.open();
                } else {
                    if (files.isEmpty()) {
                        MessageBox messageBox = new MessageBox(getShell(), SWT.ICON_INFORMATION | SWT.OK);
                        messageBox.setMessage("No files for cropping. Select anything, please.");
                        messageBox.open();
                    } else {
                        cropper.setCropTemplates(cropTemplates);
                        cropper.cropImages(files);
                    }
                }
            }
        };
        actionExit = new Action("Exit") {
            public void run() {
                close();
            }
        };
    }

    /**
     * Create the menu manager.
     * 
     * @return the menu manager
     */
    @Override
    protected MenuManager createMenuManager() {
        MenuManager menuManager = new MenuManager("menu");

        MenuManager menuManagerFile = new MenuManager("File");
        menuManager.add(menuManagerFile);
        menuManagerFile.add(new Separator());
        menuManagerFile.add(actionExit);
        return menuManager;
    }

    /**
     * Create the toolbar manager.
     * 
     * @return the toolbar manager
     */
    @Override
    protected ToolBarManager createToolBarManager(int style) {
        ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT | SWT.WRAP);
        toolBarManager.add(actionRun);
        toolBarManager.add(new Separator());
        return toolBarManager;
    }

    /**
     * Create the status line manager.
     * 
     * @return the status line manager
     */
    @Override
    protected StatusLineManager createStatusLineManager() {
        StatusLineManager statusLineManager = new StatusLineManager();
        return statusLineManager;
    }

    /**
     * Configure the shell.
     * 
     * @param newShell
     */
    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText("Crop Motion");
    }

    protected void handleShellCloseEvent() {
        settings.serialize(CropSettings.LOCATION);

        super.handleShellCloseEvent();
        setReturnCode(SWT.DEFAULT);
    }
}
